﻿#include "stdafx.h"
#include "GameObject.h"
#include "Terrain.h"

GameObject::GameObject()
{
	m_list = nullptr;
	m_parent = nullptr;
	m_id = 0;
	m_enable = false;
	m_visible = false;
    m_position = glm::vec3(0.0f);
    m_worldMatrix = glm::mat4(1.0f);
    m_localMatrix = glm::mat4(1.0f);
	m_localMatrixPtr = &m_localMatrix;
}

GameObject::~GameObject()
{
	if (m_parent) m_parent->RemoveChild(this);
	DeleteAllChild();
	if (m_list)
	{
		auto b = m_list->begin();
		auto e = m_list->end();
		for (; b != e; ++b)
		{
			if (*b == this)
			{
				m_list->erase(b);
				break;
			}
		}
	}
}

int GameObject::GetId()
{
	return m_id;
}

void GameObject::SetId(int id)
{
	m_id = id;
}
//复制矩阵
void GameObject::SetLocalMatrix(glm::mat4 &mat)
{
	*m_localMatrixPtr = mat;
}
//直接使用矩阵指针,传递nullptr则使用内部矩阵
void GameObject::SetLocalMatrixPtr(glm::mat4* mat)
{
	m_localMatrixPtr = mat ? mat : &m_localMatrix;
}

glm::mat4 &GameObject::GetLocalMatrix()
{
	return *m_localMatrixPtr;
}

void GameObject::UpdateLocalMatrix()
{
    glm::mat<4,4,float> m(1.0f);
	auto t = glm::translate(m, m_position);
	auto r = glm::rotate(t, glm::radians(m_rotate), glm::vec3(0.0f, 0.0f, 1.0f));
	auto s = glm::scale(r, glm::vec3(m_scale));
	SetLocalMatrix(s);
}

void GameObject::UpdateWorldMatrix()
{
	int size = m_intermediateMatrices.size();
	m_worldMatrix = *m_localMatrixPtr;
	for (auto m : m_intermediateMatrices)
	{
		m_worldMatrix = (*m) * m_worldMatrix;
	}
	if (m_parent) m_worldMatrix = m_parent->m_worldMatrix * m_worldMatrix;
	for (auto t : m_child)
	{
		t->UpdateWorldMatrix();
	}
}

glm::mat4 &GameObject::GetWorldMatrix()
{
	return m_worldMatrix;
}


bool GameObject::Find(GameObject *obj, std::vector<GameObject *>::iterator &result)
{
	auto it2 = m_child.end();
	result = std::find(m_child.begin(), it2, obj);
	return result != it2;
}

void GameObject::SetParent(GameObject *obj)
{
	if (m_parent)
	{
		if (m_parent != obj) m_parent->RemoveChild(this);
		else return;
	}
	if (obj)
	{
		//obj->AddChild(this);
		m_parent = obj;
		obj->m_child.push_back(this);
		if (obj->m_list && !m_list)
		{
			SetList(obj->m_list);
		}
	}
}

void GameObject::RemoveChild(GameObject *obj)
{
	std::vector<GameObject *>::iterator it;
	if (Find(obj, it))
	{
		(*it)->m_parent = nullptr;
		m_child.erase(it);
	}
}

void GameObject::RemoveAllChild()
{
	for (auto c : m_child)
	{
		c->m_parent = nullptr;
	}
	m_child.clear();
}

void GameObject::SetList(std::vector<GameObject*> *list)
{
	m_list = list;
	m_list->push_back(this);
	for (auto obj : m_child)
	{
		if (!obj->m_list) obj->SetList(list);
	}
}

void GameObject::AddChild(GameObject *obj)
{
	obj->SetParent(this);
	//if (m_list && !obj->m_list)
	//{
	//	obj->SetList(m_list);
	//}
}

void GameObject::DeleteChildFromId(int id)
{
	auto b = m_child.begin();
	auto e = m_child.end();
	for (; b != e; ++b)
	{
		if ((*b)->GetId() == id)
		{
			(*b)->m_parent = nullptr;
			delete *b;
			break;
		}
	}
}

void GameObject::DeleteChild(GameObject *obj)
{
	std::vector<GameObject *>::iterator it;
	if (Find(obj, it))
	{
		(*it)->m_parent = nullptr;
		delete (*it);
		m_child.erase(it);
	}
}

void GameObject::DeleteAllChild()
{
	for (auto t : m_child)
	{
		t->m_parent = nullptr;
		delete t;
	}
	m_child.clear();
}

int GameObject::GetChildCount()
{
	return m_child.size();
}

GameObject *GameObject::GetChild(int index)
{
	return m_child[index];
}

void GameObject::OnFocus(bool focus)
{
	if (m_cb_focus)
	{
		m_cb_focus(this, focus);
	}
}

void GameObject::OnSelect(bool select)
{
	if (m_cb_select)
	{
		m_cb_select(this, select);
	}
}

void GameObject::SetPosition(float x, float y)
{
	m_position.x = x;
	m_position.y = y;
	m_position.z = Terrain::GetInstance()->GetHeight(x, y) * 1.5f;
}

void GameObject::SetPosition(float x, float y, float z)
{
	m_position = glm::vec3(x, y, z);
	//m_localMatrix = glm::translate<float>(pos.x, pos.y, z);
}

glm::vec3 &GameObject::GetPosition()
{
	return m_position;
}

void GameObject::SetRotate(float angle)
{
	m_rotate = angle;
}

float GameObject::GetRotate()
{
	return m_rotate;
}

void GameObject::SetScale(float a)
{
	m_scale = a;
}

float GameObject::GetScale()
{
	return m_scale;
}

float GameObject::GetRadius()
{
	return m_radius;
}
